﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Net.Mime;
using System.IO;

namespace NHINDirect
{
    public class Message : MimeEntity
    {
        MailAddress m_from;
        MailAddressCollection m_to;
        
        public Message()
        {
        }
        
        public Message(IEnumerable<Header> headers)
        {
            if (headers == null)
            {
                throw new ArgumentNullException();
            }
            
            this.Headers.Add(headers);
        }
        
        public virtual Header To
        {
            get
            {
                return this.Headers[Protocol.ToHeader];
            }
            set
            {
                this.Headers[Protocol.ToHeader] = value;
                this.m_to = null;
            }
        }

        public virtual Header From
        {
            get
            {
                return this.Headers[Protocol.FromHeader];
            }
            set
            {
                this.Headers[Protocol.FromHeader] = value;
                this.m_from = null;
            }
        }

        public Header Subject
        {
            get
            {
                return this.Headers[Protocol.SubjectHeader];
            }
            set
            {
                this.Headers[Protocol.SubjectHeader] = value;
            }
        }

        public Header ID
        {
            get
            {
                return this.Headers[Protocol.MessageIDHeader];
            }
            set
            {
                this.Headers[Protocol.MessageIDHeader] = value;
            }
        }

        public Header CC
        {
            get
            {
                return this.Headers[Protocol.CCHeader];
            }
            set
            {
                this.Headers[Protocol.CCHeader] = value;
            }
        }

        public Header Date
        {
            get
            {
                return this.Headers[Protocol.DateHeader];
            }
            set
            {
                this.Headers[Protocol.DateHeader] = value;
            }
        }
        
        public MailAddressCollection ParsedTo
        {
            get
            {
                if (this.m_to == null)
                {
                    this.m_to = this.ToAddressCollection<MailAddress, MailAddressCollection>(this.To, x => new MailAddress(x));
                }
                
                return this.m_to;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }
                this.To = new Header(Protocol.ToHeader, value.ToString());
                this.m_to = value;
            }
        }
        
        public MailAddress ParsedFrom
        {
            get
            {
                if (this.m_from == null)
                {
                    this.m_from = this.ToMailAddress(this.From);
                }

                return this.m_from;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }
                
                this.From = new Header(Protocol.FromHeader, value.ToString());
                this.m_from = value;
            }
        }
        
        /// <summary>
        /// The source message has non-MIME headers
        /// Takes the source and creates new Message that contains only items relevant to Mime
        /// </summary>
        /// <returns></returns>
        public MimeEntity ExtractMimeEntity()
        {
            MimeEntity entity = new MimeEntity();
            
            if (this.HasHeaders)
            {
                //
                // TODO: Optimize this.... 
                //
                entity.Headers = this.Headers.SelectMimeHeaders();
                if (!entity.HasHeaders)
                {
                    throw new ProtocolException(ProtocolError.InvalidMimeEntity);
                }
            }
            
            if (this.HasBody)
            {
                entity.Body = new Body(this.Body);
            }
            
            return entity;
        }
        
        public MimeEntity ExtractEntityForSignature(bool includeEpilogue)
        {
            if (includeEpilogue || !this.IsMultiPart)
            {
                return this.ExtractMimeEntity();
            }
            
            MimeEntity signableEntity = new MimeEntity();
            signableEntity.Headers = this.Headers.SelectMimeHeaders();
            
            StringSegment content = StringSegment.Null;
            foreach(EntityPart part in this.GetAllParts())
            {
                if (part.Type == EntityPartType.Epilogue)
                {
                    content = new StringSegment(content.Source, content.StartIndex, part.SourceText.StartIndex - 1);
                }
                else
                {                
                    content.Union(part.SourceText);
                }
            }            
            signableEntity.Body = new Body(content);
            
            return signableEntity;
        }
                
        MailAddress ToMailAddress(Header header)
        {
            if (header == null)
            {
                return null;
            }

            return new MailAddress(header.Value);
        }

        protected TCollection ToAddressCollection<T, TCollection>(Header header, Func<string, T> constructor)
            where T : MailAddress
            where TCollection : Collection<T>, new()
        {
            if (header == null)
            {
                return null;
            }
            
            TCollection collection = new TCollection();
            foreach (StringSegment part in EntityParser.ReadHeaderParts(header.Value, Protocol.MailAddressSeparator))
            {
                collection.Add(constructor(EntityParser.SkipWhitespace(part).ToString()));
            }
            
            return collection;
        }
    }
}
